<template>
  <transition leave-active-class="animated fadeOut" enter-active-class="animated fadeIn">
    <template v-if="JSON.parse(shade)">
      <div ref="overlayRef" v-if="valIf" v-show="valShow" class="vui__overlay" @click="shadeClicked"></div>
    </template>
  </transition>
  <transition :leave-active-class="leaveActiveClass" :enter-active-class="enterActiveClass">
    <div 
    v-if="valIf" 
    v-show="valShow" 
    ref="wrapRef" 
    class="vui__wrap" 
    @touchend.passive="drag&&touchEnd($event)" 
    @touchstart.passive="drag&&touchStart($event)" 
    @touchmove.passive="drag&&touchMove($event)"
    @touchcancel.passive="drag&&touchEnd($event)"
    :class="[popupClass,round&&'round', position&&'vui__wrap--'+position]" 
    :style="[popupStyle]">
        <div v-if="title" class="vui__wrap-tit" v-html="title"></div>
        <template v-if="hasSlot('title')">
            <div class="vui__wrap-tit"><slot name="title"/></div>
        </template>
        <template v-if="component">
            <div class="vui__wrap-content" :class="[...contentClass]">
              <component :is="component" />
            </div>
        </template>
        <template v-else-if="content">
            <div class="vui__wrap-content" :class="[...contentClass]" v-html="content"></div>
        </template>
        <template v-else>
            <div class="vui__wrap-content" :class="[...contentClass]"><slot /></div>
        </template>
        <div v-if="btns" class="vui__wrap-footer round" :class="[position=='bottom'&&'popupui__bottom']">
            <span v-for="(btn, index) in btns" :key="index" class="vui__btn" :class="[btn.type&&'vui__btn--'+btn.type,btn.class]" :style="btn.style" @click="btnClicked($event, index)" v-html="btn.text"></span>
        </div>
        <span v-if="xclose" class="vui__xclose" :class="xposition" @click="onClose">
          <svg t="1610938797571" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2665" width="200" height="200"><path fill="currentColor"  d="M583.168 523.776L958.464 148.48c18.944-18.944 18.944-50.176 0-69.12l-2.048-2.048c-18.944-18.944-50.176-18.944-69.12 0L512 453.12 136.704 77.312c-18.944-18.944-50.176-18.944-69.12 0l-2.048 2.048c-19.456 18.944-19.456 50.176 0 69.12l375.296 375.296L65.536 899.072c-18.944 18.944-18.944 50.176 0 69.12l2.048 2.048c18.944 18.944 50.176 18.944 69.12 0L512 594.944 887.296 970.24c18.944 18.944 50.176 18.944 69.12 0l2.048-2.048c18.944-18.944 18.944-50.176 0-69.12L583.168 523.776z" p-id="2666"></path></svg>
        </span>
    </div>
  </transition>
</template>


<script>
import { upperFirst, debounce } from 'lodash'

import { onMounted, ref, reactive, watch, nextTick, computed, onUnmounted, onBeforeUnmount,h } from "vue";
import { onBeforeRouteLeave, onBeforeRouteUpdate, useRouter } from 'vue-router';

let $index = 0, $timer = {};
//获取指定节点的css指定样式
const getStyle = (dom,styleProp) =>{
  if (window.getComputedStyle) {
    const style = document.defaultView.getComputedStyle(dom,null); 
    return style.getPropertyValue(styleProp)||style[styleProp]
  } else if (dom.currentStyle){
    return dom.currentStyle[styleProp];
  }
}
//获取最大 z-index
function getMaxZIndex() {
  const nodeList = document.querySelectorAll('*')
  const maxZ = Math.max.apply(null,[...nodeList].map((item,i)=>{
    const zId = getStyle(item,'zIndex')
    return zId&&zId != "auto" ? Number(zId) : -1
  }))
  return maxZ;
}
export default {
  props: {
    modelValue: { type: Boolean, default: false },
    // 标识符，相同ID共享一个实例
    id: { type: String, default: "",},
    component: { type: Object, default: null },
    title: String,
    content: String,
    textAlign: { type: String, default: "left" },
    popupStyle: String,
    popupClass: String,
    cententClass: String,
    shade: { type: [Boolean, String], default: true },
    shadeClose: { type: [Boolean, String], default: true },
    // opacity: { type: [Number, String], default: .5 },
    round: Boolean,
    xclose: { type: Boolean, default: true },
    drag: { type: Boolean, default: false },
    backOver: { type: Boolean, default: true },
    duration: { type: Number, default: 200 },
    xposition: { type: String, default: "right" },
    xcolor: { type: String, default: "#333" },
    position: { type: String, default: "center" },
    follow: { type: Array, default: null },
    time: { type: [Number, String], default: 0 },
    zIndex: { type: [Number, String], default: "8080" },
    teleport: [String, Object],
    dragDeg: { type: Number, default: 30 },
    btns: {
      type: Array,
      default: null,
    },
    remove: { type: Boolean, default: false },
    removed: { type: Function, default: null },
    onSuccess: { type: Function, default: null },
    onEnd: { type: Function, default: null },
  },
  emits: ["update:modelValue"],
  setup(props, context) {
    const router = useRouter()
    const overlayRef = ref(null);
    const wrapRef = ref(null);
    const upperFt = (str) =>{
      return upperFirst(str)
    }

    const data = reactive({
      opened: false,
      toastIcon: {},
    });

    onMounted(() => {
        wrapRef.value&&(wrapRef.value.style.animationDuration = `${props.duration}ms`)
        overlayRef.value&&(overlayRef.value.style.animationDuration = `${props.duration}ms`)
        if (props.modelValue) {
          open();
        } else {
          onClose();
        }
    });

    // 计算 
    const valIf = computed(() => (props.remove&&data.opened)||!props.remove)
    const valShow = computed(() => (!props.remove&&data.opened)||props.remove)
    const leaveActiveClass = computed(() => props.position&&props.position!=='center'?`animated popSlide${upperFt(props.position)}Out`:'animated popFadeOut')
    const enterActiveClass = computed(() => props.position&&props.position!=='center'?`animated popSlide${upperFt(props.position)}In`:'animated popSlideIn')
    const contentClass = computed(() => [props.cententClass,!(props.title||hasSlot('title'))?(props.xclose&&props.xposition=='right'?'vui__wrap-pt45':'vui__wrap-pt16'):'',!props.xclose&&props.position=='bottom'&&'popupui__bottom', props.textAlign&&'vui__text--'+props.textAlign])
    
    // 监听弹层v-model
    watch(
      () => props.modelValue,
      (val) => {
        if (val) {
          open();
        } else {
          onClose();
        }
      }
    );

    //render 渲染
    if(props.render){
      typeof props.render === "function" && props.render(()=>{
        h()
      });
    }

    function getTranslateXY(matrix){
      const val = matrix&&matrix.match(/^matrix(3d)?(.*)$/i)
      if(val && val[2]){
        const tmp = val[2].replace(/ /g, '').split(',');
        translate.x = parseInt(tmp[val[1] ? 12 : 4], 10)
        translate.y = parseInt(tmp[val[1] ? 13 : 5], 10)
      }
    }

    function setTranslate(){
      wrapRef.value&&getTranslateXY(getStyle(wrapRef.value, "transform" ))
      hideEventsAni()
    }
    function addEventsAni(){
      ['webkitAnimationEnd','animationend'].forEach(item=>{
        wrapRef.value&&wrapRef.value.addEventListener(item, setTranslate)
      })
    }
    function hideEventsAni(){
      ['webkitAnimationEnd','animationend'].forEach(item=>{
        wrapRef.value&&wrapRef.value.removeEventListener(item, setTranslate)
      })
    }
    // 回退事件
    // 当前组件路由改变后，进行触发
    const backOver = (to,from,next)=> {
        if(props.backOver&&data.opened){
          window.history.pushState(null,'',from.path)
          onClose()
        }else{
          next()
        }
    }
    onBeforeRouteUpdate(backOver)
    onBeforeRouteLeave(backOver)
    
    // 打开弹层
    const open = () => {
      if (data.opened) return;
      data.opened = true;

      typeof props.onSuccess === "function" && props.onSuccess();
      nextTick(()=>{
        let zInd = +getMaxZIndex();
        overlayRef.value.style.zIndex = zInd + 1;
        wrapRef.value.style.zIndex = zInd + 2;

        props.drag&&addEventsAni()
        // 倒计时
        if (props.time) {
          $index++;
          // 避免重复操作
          if ($timer[$index] !== null) clearTimeout($timer[$index]);
          $timer[$index] = setTimeout(() => {
            onClose();
          }, parseInt(props.time) * 1000);
        }
      })
    };

    // 关闭弹层
    const onClose = () => {
      if($timer[$index]){
        clearTimeout($timer[$index])
      }
      if(wrapRef.value){
        wrapRef.value.style.animationDuration = `${props.duration}ms`;
      }
      close()
    }
    const close = async () => {
      if (!data.opened) return;
      data.opened = false
      typeof props.onEnd === "function" && props.onEnd();
      await debounce(async () => {
        context.emit("update:modelValue", false);
        props.remove&&props.removed&&typeof props.removed === "function"&& props.removed()
        if(wrapRef.value&&wrapRef.value.style){
          wrapRef.value.style.WebkitTransform = ''
          wrapRef.value.style.animationDuration = `${props.duration}ms`;
        }
        return Promise.resolve()
      },200)()
    };

    // 点击遮罩层
    const shadeClicked = () => {
      if (JSON.parse(props.shadeClose)) {
        onClose();
      }
    };
    // 按钮事件
    const btnClicked = (e, index) => {
      let btn = props.btns[index];
      if (!btn.disabled) {
        typeof btn.click === "function" && btn.click(e);
      }
    };
    
    function hasSlot(name){
      return context.slots[name]?true:false
    }

    //拖拽
    const translate = reactive({
      x:0,y:0,z:0
    });
    let statusTouches = []
    let moveStatus = false;
    //判断移动方向 上下还是左右
    function getDirection(x,y){
        let dx = Math.abs(x) - Math.abs(y);
        return dx > 0 ? ((x > 0) ? 'right' : 'left') : ((y > 0) ? 'bottom' : 'top')
    }
    //拷贝一个触摸对象
    function copyMoveouch(touch,timeStamp,stats) {
        return { 
            identifier: touch.identifier, 
            pageX: touch.pageX, 
            pageY: touch.pageY,
            startX: stats ? stats.startX : touch.pageX,
            startY: stats ? stats.startY : touch.pageY,
            stamp: stats ? stats.stamp : timeStamp,
            stime: stats ? (timeStamp - stats.stamp) : 0,
        };
    }
    //找出正在进行的触摸
    function statusToucheIndexById(idToFind) {
        for (var i=0; i < statusTouches.length; i++) {
            let id = statusTouches[i].identifier;
            if (id == idToFind) {
                return i;
            }
        }
        return -1;    // not found
    }
    const draw = (x=0, y=0) => {
      let x1,y1
      if(!props.position){
        return
      }
      if(props.position&&props.position=='left'){
        x1 = translate.x + x > translate.x ? translate.x : translate.x + x
        y1 = translate.y
      }
      if(props.position&&props.position=='right'){
        x1 = translate.x + x < translate.x ? translate.x : translate.x + x
        y1 = translate.y
      }
      if(props.position&&props.position=='bottom'){
        x1 = translate.x
        y1 = translate.y + y < translate.y ? translate.y : translate.y + y
      }
      if(props.position&&props.position=='top'){
        x1 = translate.x
        y1 = translate.y + y > translate.y ? translate.y : translate.y + y
      }
      if(props.position&&props.position=='center'){
        const directionX = x > 0 ? 1 : -1;
        const directionY = y > 0 ? 1 : -1;
        const ow = overlayRef.value.clientWidth
        const pw = wrapRef.value.clientWidth
        const oh = overlayRef.value.clientHeight
        const ph = wrapRef.value.clientHeight
        const _y = wrapRef.value.offsetTop - wrapRef.value.scrollTop;
        x1 = Math.abs(x) > (ow - pw)/2 ? translate.x + directionX*(ow - pw)/2 : translate.x + x
        const yKey = directionY>0 ? oh - (_y + ph) - translate.y : _y - directionY*translate.y
        y = Math.abs(y) > yKey ? directionY*yKey : y
        y1 = translate.y + y

      }
      wrapRef.value.style.WebkitTransform = `translate(${x1}px, ${y1}px)`
    }

    const touchStart = (event) => {
      event.stopPropagation()
      moveStatus = false
      let touches = event.changedTouches;
      for (let i=0; i < touches.length; i++) {
          let idx = statusToucheIndexById(touches[i].identifier);
          if( idx >= 0) {
            statusTouches.splice(idx, 1, copyMoveouch(touches[i],event.timeStamp));
          }else{
            statusTouches.push(copyMoveouch(touches[i],event.timeStamp));
          }
      }
      wrapRef.value.style.transitionDuration = '0s';
      wrapRef.value.style.animationDuration = '0s';
    }

    const touchMove = (event) => {
      event.stopPropagation()
      let touches = event.changedTouches;
        for (let i=0; i < touches.length; i++) {
          let idx = statusToucheIndexById(touches[i].identifier);
          if( idx >= 0) {
            statusTouches.splice(idx, 1, copyMoveouch(touches[i],event.timeStamp,statusTouches[i]));
          }
        }
        let xx = statusTouches[0].pageX - statusTouches[0].startX
        let yy = statusTouches[0].pageY - statusTouches[0].startY
        const dir = getDirection(xx,yy)
        if(props.position && props.position == "center"){
          moveStatus = true
        }
        if(!moveStatus && statusTouches[0].stime < 100){
          moveStatus = (dir == props.position && Math.abs(yy/xx)<Math.tan(props.dragDeg*0.017453293))
        }
        moveStatus && draw(xx, yy);
    }


    const touchEnd = (event) => {
      let tmi = 0
      const ow = overlayRef.value.clientWidth
      const oh = overlayRef.value.clientHeight
      event.stopPropagation()
      const touches = event.changedTouches;
      for (let i=0; i < touches.length; i++) {
          let idx = statusToucheIndexById(touches[i].identifier);
          if(idx >= 0) {
            statusTouches.splice(idx, 1, copyMoveouch(touches[i],event.timeStamp,statusTouches[i]));
            let x = statusTouches[i].pageX - statusTouches[i].startX
            let y = statusTouches[i].pageY - statusTouches[i].startY

            const directionX = x > 0 ? 1 : -1;
            const directionY = y > 0 ? 1 : -1;
            const absX = Math.abs(x);
            const absY = Math.abs(y);
            const tepX =  absX / Math.floor(statusTouches[i].stime) > 0.65
            const tepY =  absY / Math.floor(statusTouches[i].stime) > 0.65
            if(moveStatus&&props.position&&(props.position=='left'||props.position=='right')){
              tmi = (props.duration * absX) / ow
            }
            if(props.position&&((absX>wrapRef.value.clientWidth * 3/5) || (tepX && statusTouches[i].stime<200))){
              if(
                (directionX<0&&props.position=='left')||
                (directionX>0&&props.position=='right')){
                moveStatus&&close()
              }
            }

            if(props.position&&((absY>wrapRef.value.clientHeight * 1/2)||(tepY && statusTouches[i].stime<200))){
              if((directionY<0&&props.position=='top')||
                (directionY>0&&props.position=='bottom')){
                moveStatus&&close()
              }
            }

            statusTouches.splice(idx, 1);
          }
      }
      statusTouches = []
      moveStatus = false;
      draw(0,0)
      // if(!props.position||props.position=='center') {
      //   getTranslateXY(getStyle(wrapRef.value, "transform" ))
      // }else{
      //   draw(0,0)
      // }

      tmi = tmi<100 ? 100 : tmi
      wrapRef.value.style.transitionDuration = `${tmi}ms`;
    }

    return {
      valIf,
      valShow,
      leaveActiveClass,
      enterActiveClass,
      contentClass,
      hasSlot,
      overlayRef, wrapRef,
      onClose,
      shadeClicked,
      btnClicked,
      upperFt,
      touchMove,
      touchStart,
      touchEnd
    };
  },
};
</script>
<style lang="less" scoped>
@import 'popup.less';
</style>
